package com.whately.core.base.swagger;

import cn.hutool.crypto.digest.DigestUtil;
import com.google.common.base.Function;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ApiListingBuilder;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.ApiListingBuilderPlugin;
import springfox.documentation.spi.service.contexts.ApiListingContext;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import static springfox.documentation.spring.web.paths.Paths.splitCamelCase;

@Component
@Order(value= Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class SwaggerConfigApiListing4ChangePathWithMethodReader implements ApiListingBuilderPlugin {

    @Value("${swagger.config.whately.changepath:true}")
    private boolean changePathUsingMethod;

    @Value("${swagger.config.whately.checkmethodparam:true}")
    private boolean checkMethodParam;

    /**
     * 1.api 的路径不允许是 / ,至少是一层 ,比如/xx
     * 2.api requestMapping 必须要包含method参数
     */
    @Override
    public void apply(ApiListingContext apiListingContext) {
        log.info("SwaggerConfigApiListing4ChangePathWithMethodReader changePathUsingMethod enter ,changePathUsingMethod is {}",changePathUsingMethod);
        if(!changePathUsingMethod){
            log.info("SwaggerConfigApiListing4ChangePathWithMethodReader changePathUsingMethod return ");
            return;
        }
        ApiListingBuilder apiListingBuilder = apiListingContext.apiListingBuilder();
        List<ApiDescription> apiDescriptionsNew = new ArrayList<>();

        ApiListing apiListing = apiListingContext.apiListingBuilder().build();
        int count=0;
        for(ApiDescription old:apiListing.getApis()){
            String oldPath=old.getPath();
            if(StringUtils.isBlank(oldPath)||old.equals("/")){
                throw new RuntimeException("request mapping config error !!!!!  path is blank! not allow ! class is "+old.getGroupName().get());
            }
            List<Operation> operationList = old.getOperations();
            String methodNameValue = null;
            String methodName = null;

            String orderStr=StringUtils.EMPTY;
            if(operationList!=null&&operationList.size()>0){

                Operation operation = operationList.get(0);

                int order = Integer.MAX_VALUE;
                List<VendorExtension> veList1 = operation.getVendorExtensions();
                for(VendorExtension ve:veList1){
                    if(StringUtils.equals("x-order",ve.getName())){
                        try{
                            order = Integer.parseInt(String.valueOf(ve.getValue()));
                        }catch (Exception e){
                            log.error("swagger error",e);
                        }
                        break;
                    }
                }

                //2147483647
                String PRE = "999999999";

                if(order< Integer.MAX_VALUE){
                    orderStr = StringUtils.leftPad(String.valueOf(order),PRE.length(),"0");
                }else{
                    orderStr = PRE;
                }

                //取参数method=xxx
                List<Parameter> parameterList = operation.getParameters();
                if(parameterList!=null&&parameterList.size()>0){
                    for(Parameter parameter:parameterList){
                        String p_name = parameter.getName();
                        if("method".equals(p_name)){
                            methodName = p_name;
                            methodNameValue = parameter.getDefaultValue();
                        }
                    }
                }
            }

            if(checkMethodParam&&StringUtils.isBlank(methodNameValue)){
                throw new RuntimeException("request mapping config error !!!!!  not found param method is path "+methodName+",class is "+old.getGroupName().get());
            }

            if(StringUtils.isNotBlank(methodNameValue)){
                String suffix =  "?ignore="+ orderStr+ DigestUtil.md5Hex(old.getDescription()+old.getGroupName())+ RandomStringUtils.randomNumeric(6,6) +(count++);
                if(!oldPath.endsWith("/")){
                    suffix = "/"+suffix;
                }
                String newpath = oldPath+suffix;
                ApiDescription apiDescriptionChangePath = new ApiDescription(old.getGroupName().get(),newpath,old.getDescription(),old.getOperations(),old.isHidden());
                apiDescriptionsNew.add(apiDescriptionChangePath);
            }else{
                apiDescriptionsNew.add(old);
            }

        }

        apiListingBuilder.apis(apiDescriptionsNew);
        log.info("SwaggerConfigApiListing4ChangePathWithMethodReader changePathUsingMethod over ");
    }

    private Function<Class<?>, String> description() {
        return new Function<Class<?>, String>() {
            @Override
            public String apply(Class<?> input) {
                return splitCamelCase(input.getSimpleName(), " ");
            }
        };
    }


    @Override
    public boolean supports(DocumentationType delimiter) {
        return true;
    }
}
